#ifndef _POWER_H_
#define _POWER_H_
#include "sdkconfig.h"
#include "esp_err.h"
#include "driver/i2c.h"
#include "driver/gpio.h"
#include "esp_log.h"

// ADC includes
#include "soc/soc_caps.h"
#include "esp_adc/adc_oneshot.h"
#include "esp_adc/adc_cali.h"
#include "esp_adc/adc_cali_scheme.h"

// ================ I2C ===================

#define I2C_MASTER_SCL_IO CONFIG_I2C_MASTER_SCL /*!< GPIO number used for I2C master clock */
#define I2C_MASTER_SDA_IO CONFIG_I2C_MASTER_SDA /*!< GPIO number used for I2C master data  */
#define I2C_MASTER_NUM I2C_NUM_0                /*!< I2C master i2c port number, the number of i2c peripheral interfaces available will depend on the chip */
#define I2C_MASTER_FREQ_HZ 400000               /*!< I2C master clock frequency */
#define I2C_MASTER_TX_BUF_DISABLE 0             /*!< I2C master doesn't need buffer */
#define I2C_MASTER_RX_BUF_DISABLE 0             /*!< I2C master doesn't need buffer */
#define I2C_MASTER_TIMEOUT_MS 1000

// ================= GPIO ===================
#define INT_5306 GPIO_NUM_10
#define POWER_KEY GPIO_NUM_4
// ================ Power IC IP5306 ===================
#define IP5306_ADDR (0x75)
#define IP5306_REG_SYS_CTL0 (0x00)
#define IP5306_REG_SYS_CTL1 (0x01)
#define IP5306_REG_SYS_CTL2 (0x02)
#define IP5306_REG_Charger_CTL0 (0x20)
#define IP5306_REG_Charger_CTL1 (0x21)
#define IP5306_REG_Charger_CTL2 (0x22)
#define IP5306_REG_Charger_CTL3 (0x23)
#define IP5306_REG_CHG_DIG_CTL0 (0x24)
#define IP5306_REG_READ0 (0x70)
#define IP5306_REG_READ1 (0x71)
#define IP5306_REG_READ2 (0x72)
#define IP5306_REG_READ3 (0x78)

// REG_CTL0
#define BOOST_ENABLE_BIT (0x20)
#define CHARGE_OUT_BIT (0x10)
#define BOOST_ON_LOAD_BIT (0x04)
#define BOOST_OUT_BIT (0x02)
#define BOOST_BUTTON_EN_BIT (0x01)

//- REG_CTL1
#define BOOST_SET_BIT (0x80)
#define WLED_SET_BIT (0x40)
#define SHORT_BOOST_BIT (0x20)
#define VIN_ENABLE_BIT (0x04)
#define LOWPOWER_SHUTDOWN_BIT (0x01)

//- REG_CTL2
#define SHUTDOWNTIME_MASK (0x0c)
#define SHUTDOWNTIME_64S (0x0c)
#define SHUTDOWNTIME_32S (0x04)
#define SHUTDOWNTIME_16S (0x08)
#define SHUTDOWNTIME_8S (0x00)

//- REG_READ0
#define CHARGE_ENABLE_BIT (0x08)

//- REG_READ1
#define CHARGE_FULL_BIT (0x08)

//- REG_READ2
#define LIGHT_LOAD_BIT (0x20)
#define LOWPOWER_SHUTDOWN_BIT (0x01)

//- CHG
#define CURRENT_100MA (0x01 << 0)
#define CURRENT_200MA (0x01 << 1)
#define CURRENT_400MA (0x01 << 2)
#define CURRENT_800MA (0x01 << 3)
#define CURRENT_1600MA (0x01 << 4)

#define BAT_4_2V (0x00)
#define BAT_4_3V (0x01)
#define BAT_4_3_5V (0x02)
#define BAT_4_4V (0x03)

#define CHG_CC_BIT (0x20)

#define LIGHT_LOAD_BIT (0x20)

// Add voltage marcros
#define ADD_VOLTAGE_0MV (0x00)
#define ADD_VOLTAGE_14MV (0x01)
#define ADD_VOLTAGE_28MV (0x02)
#define ADD_VOLTAGE_42MV (0x03)

// ADC Channels
#define ADC_CHAN ADC_CHANNEL_0
#define ADC_ATTEN ADC_ATTEN_DB_11


//NU1680
#define WirelessCharge_Device_Addr  (0x60)/* 无线充电IC的设备地址 */
/* ADC Channel Registers */
#define IOUT_FILT    				0x30
#define VRECT_FILT     				0x34
/* Parameter Configure Registers  */
#define OTP_FLAG      			    0x06

// ================ enum ===================
enum enum_charge_state
{
    NOT_CHARGING,
    CHARGING,
    FULL,
    ERROR,
};

typedef uint8_t charge_state_t;

// Power Class
class Power
{
private:
    /**
     * @brief Initialize ADC calibration
     * @param unit ADC unit
     * @param channel ADC channel
     * @param atten ADC attenuation
     * @param out_handle Pointer to calibration handle
     * @return true if calibration is initialized successfully, false otherwise
     */
    static bool __adcCalibrationInit(adc_unit_t unit, adc_channel_t channel, adc_atten_t atten, adc_cali_handle_t *out_handle);
    /**
     * @brief Initialize the i2c bus
     * @return esp_err_t ESP_OK if successful, ESP_FAIL otherwise
     */
    static esp_err_t __initI2C();
    /**
     * @brief Initialize the IP5306 power management IC
     * @return esp_err_t ESP_OK if successful, ESP_FAIL otherwise
     */
    static esp_err_t __initIP5306();
    /**
     * @brief Read a byte from the i2c register
     * @param reg The register to read from
     * @param data The data read from the register
     * @return esp_err_t ESP_OK if successful, ESP_FAIL otherwise
     */
    static esp_err_t __readRegister(uint8_t address, uint8_t reg, uint8_t *data);
    /**
     * @brief Write a byte to the i2c register
     * @param reg The register to write to
     * @param data The data to write to the register
     * @return esp_err_t ESP_OK if successful, ESP_FAIL otherwise
     */
    static esp_err_t __writeRegister(uint8_t address, uint8_t reg, uint8_t data);

    static void __daemonChargeStateCheck(void *pvParameters);

    static SemaphoreHandle_t __i2c_mutex;

    static bool __do_calibration1_chan0;
    static adc_cali_handle_t __adc1_cali_chan0_handle;
    static adc_oneshot_unit_handle_t __adc_handle;

public:
    static charge_state_t charge_state;
    Power();
    /**
     * @brief Fetch the current level of the battery
     * @return uint8_t The battery level in percentage
     */
    static uint8_t getBatteryLevel();
    /**
     * @brief Get the voltage of VOUT pin using ADC
     * @return uint16_t The voltage in mV
     */
    static uint16_t getBatteryVoltage();
    /**
     * @brief Get charge state of the battery
     * @return charge_state_t The charge state of the battery
     */
    static charge_state_t getChargeState();
    /**
     * @brief Get BOOST state
     * @return bool The state of the BOOST
     */
    static bool getBoostState();
    /**
     * @brief Get the current charge current
     * @return uint16_t The charge current in mA
     */ 
    static float getWirelessChargeCurrent();
    /**
     * @brief Get the current charge voltage
     * @return uint16_t The charge voltage in mV
     */
    static float getWirelessChargeVoltage();
    /**
     * @brief get the temperature of the wireless charging IC
     * @return uint16_t The temperature in degree Celsius
     */
    static bool getWirelessChargeTemperature();
    /**
     * @brief Switch the BOOST state
     * @param state The state to switch to
     * @return esp_err_t ESP_OK if successful, ESP_FAIL otherwise
     */
    static esp_err_t setBoostState(bool state);
};

#endif